<div id="agents" class="section-profile">
  <div class="row">
    <div class="topleft duk-icon"><img onclick="removeSection('agents')" src="/gui/img/x.png"></div>
    <div class="bottomright duk-icon"><img onclick="toggleSidebar('agents-sidebar')" src="/gui/img/expand.png"></div>
    <div id="agents-sidebar" class="column section-border" style="flex:25%;">
      <img src="/gui/img/group.png">
      <h4 style="margin-bottom:-5px">Agents</h4>
      <p class="section-description">
          You must deploy at least 1 agent in order to run an operation.Groups are collections of agents so hosts can
          be compromised simultaneously.
      </p>
      <button type="button" class="button-orange atomic-button" onclick="$('#delivery-modal').show();">Click here to deploy an agent</button>
      <div id="agent-beacon" class="sidebar-header">
          <h5>Beacon Timers</h5>
      </div>
      <div id="agent-beacon-options" class="sidebar-cutout" style="display: none">
          <input id="globalSleepMin" type="text" placeholder="Beacon min (seconds)" value="{{ agent_config.sleep_min }}" />
          <input id="globalSleepMax" type="text" placeholder="Beacon max (seconds)" value="{{ agent_config.sleep_max }}" />
      </div>
      <div id="agent-watchdog" class="sidebar-header">
          <h5>Watchdog Timer</h5>
      </div>
      <div id="agent-watchdog-options" class="sidebar-cutout" style="display: none">
          <input id="globalWatchdog" type="text" placeholder="Watchdog (seconds)" value="{{ agent_config.watchdog }}" />
      </div>
      <div id="agent-untrusted" class="sidebar-header">
          <h5>Untrusted Timer</h5>
      </div>
      <div id="agent-untrusted-options" class="sidebar-cutout" style="display: none">
          <input id="globalUntrusted" type="text" placeholder="Untrusted (seconds)" value="{{ agent_config.untrusted_timer }}" />
      </div>
      <div id="agent-implant" class="sidebar-header">
          <h5>Implant Name</h5>
      </div>
      <div id="agent-implant-options" class="sidebar-cutout" style="display: none">
          <input id="globalImplantName" type="text" placeholder="Agent filename" value="{{ agent_config.implant_name }}" />
      </div>
      <div id="agent-bootstrap" class="sidebar-header">
          <h5>Bootstrap Abilities</h5>
      </div>
      <div id="agent-bootstrap-options" class="sidebar-cutout" style="display: none">
          <input id="globalBootstrap" type="text" placeholder="Bootstrap ability IDs" value="{{ agent_config.bootstrap_abilities | join(', ') }}"/>
      </div>
      <div id="agent-deadman" class="sidebar-header">
          <h5>Deadman Abilities</h5>
      </div>
      <div id="agent-deadman-options" class="sidebar-cutout" style="display: none">
          <input id="globalDeadman" type="text" placeholder="Deadman ability IDs" value="{{ agent_config.deadman_abilities | join(', ') }}"/>
      </div>
      <button id="saveAgentBtn" type="button" class="button-success atomic-button separated-button" onclick="saveGlobalAgent()">Save changes</button>
    </div>
    <div class="column" style="flex:75%">
        <span style="text-align:left">You have <span id="agent-cnt">{{ agents|length}}</span></span> agents</h4>
        <hr>
        <table id="agent-table" class="obfuscation-table" border=1 frame=void rules=rows style="border-spacing:15px;">
            <thead>
                <tr>
                    <td><b>id (paw)</b></td>
                    <td><b>host</b></td>
                    <td><b>contact</b></td>
                    <td><b>pid</b></td>
                    <td><b>privilege</b></td>
                    <td></td>
                </tr>
            </thead>
            {% for a in agents %}
                <tr id="{{ a.paw }}">
                    <td><p>{{ a.paw }}</p></td>
                    <td><p>{{ a.host }}</p></td>
                    <td><p>{{ a.contact }}</p></td>
                    {% if a.trusted %}
                        {% if a.watchdog > 0 and a.watchdog < a.sleep_min %}
                            {% set agentStatus = "yellow" %}
                        {% else %}
                            {% set agentStatus = "" %}
                        {% endif %}
                    {% else %}
                        {% set agentStatus = "maroon" %}
                    {% endif %}
                    <td><button id="{{a.paw|e}}-pid" class="button-row {{ agentStatus }}" onclick="showAgentInfo('{{ a.paw|e }}')">{{ a.pid }}</button></td>
                    <td><p>{{ a.privilege }}</p></td>
                    <td><p onclick="deleteAgent('{{ a.paw }}')">&#x274C;</p></td>
                </tr>
            {% endfor %}
        </table>
    </div>

  </div>
</div>

<div id="agent-modal" class="modal">
    <form class="modal-content">
        <div class="container section-profile row ability-viewer modal-box">
            <div class="column" style="flex:100%;">
                <h3 id="modal-paw"></h3>
                <p style="font-size: 11px">* Property can be updated</p>
                <hr>
                <table class="obfuscation-table" border=1 frame=void rules=rows>
                    <tr>
                        <td>Contact *</td>
                        <td>
                             <select id="modal-contact" value=""/>
                        </td>
                    </tr>
                    <tr>
                        <td>Host</td>
                        <td><p id="modal-host"></p></td>
                    </tr>
                    <tr>
                        <td>Username</td>
                        <td><p id="modal-username"></p></td>
                    </tr>
                    <tr>
                        <td>Privilege</td>
                        <td><p id="modal-privilege"></p></td>
                    </tr>
                    <tr>
                        <td>Last seen</td>
                        <td><p id="modal-last_seen"></p></td>
                    </tr>
                    <tr>
                        <td>Created</td>
                        <td><p id="modal-created"></p></td>
                    </tr>
                    <tr>
                        <td>Group *</td>
                        <td><input id="modal-group" style="text-align: left"/></td>
                    </tr>
                    <tr>
                        <td>Sleep *</td>
                        <td><input id="modal-sleep" style="text-align: left"/></td>
                    </tr>
                    <tr>
                        <td>Watchdog *</td>
                        <td><input id="modal-watchdog" style="text-align: left"/></td>
                    </tr>
                    <tr>
                        <td>Architecture</td>
                        <td><p id="modal-architecture"></p></td>
                    </tr>
                    <tr>
                        <td>Platform</td>
                        <td><p id="modal-platform"></p></td>
                    </tr>
                    <tr>
                        <td>PID</td>
                        <td><p id="modal-pid"></p></td>
                    </tr>
                    <tr>
                        <td>PPID</td>
                        <td><p id="modal-ppid"></p></td>
                    </tr>
                    <tr>
                        <td>Executable name</td>
                        <td><p id="modal-exe_name"></p></td>
                    </tr>
                    <tr>
                        <td>Location</td>
                        <td><p id="modal-location"></p></td>
                    </tr>
                    <tr>
                        <td>Executors</td>
                        <td><p id="modal-executors"></p></td>
                    </tr>
                    <tr>
                        <td>Host IP Addresses</td>
                        <td><p id="modal-host_ip_addrs"></p></td>
                    </tr>
                    <tr>
                        <td>Peer-to-Peer Proxy Receivers</td>
                        <td><p id="modal-peer_receivers"></p></td>
                    </tr>
                    <tr>
                        <td>Peer-to-Peer Proxy Chain</td>
                        <td><p id="modal-peer_chain"></p></td>
                    </tr>
                </table>
                <br>
                <button type="button" class="button-warn" onclick="killAgent()">Kill Agent</button>
                <button type="button" class="button-row" onclick="updateAgent()">Save</button>
            </div>
        <div class="imgcontainer">
            <span onclick="document.getElementById('agent-modal').style.display='none'" class="close" title="Close Modal">&times;</span>
        </div>
        </div>
    </form>
</div>

<div id="delivery-modal" class="modal">
    <form class="modal-content" style="width:90%;">
        <div class="container modal-box">
            <div class="row">
                <span onclick="document.getElementById('delivery-modal').style.display='none'" class="close" title="Close Modal">&times;</span>
                <div class="column" style="flex:92%">
                    <select id="chosen-agent" onchange="displayCommand()" style="width:33%;">
                        <option disabled selected>Choose an agent</option>
                        {% for i,a in abilities.items() %}
                        <option value="{{ i }}">{{ a[0].name }}:&nbsp;&nbsp;{{ a[0].description }}</option>
                        {% endfor %}}
                    </select>
                    <br>
                    <select id="chosen-platform" onchange="filterCommands()" style="width:33%;">
                        <option selected value="">All platforms</option>
                    </select>
                    <p style="font-size:12px;color:white">** Variations of the deployment command will be shown for each supported operating system</p>
                    <table id="conf-arguments" style="margin:auto; padding-top: 25px; display: none"></table>
                    <ul id="variations"></ul>
                    </div>
                </div>
            </div>
        </div>
    </form>
</div>

<div id="variation-template" class="variation-template" style="display:none;">
    <div>
        <table style="border-spacing:15px;color:white;">
            <tr>
                <td><img id="platform-icon" class="golden"/></td>
                <td><h4 id="description" style="padding-top:15px;"></h4></td>
            </tr>
            <tr>
                <td></td>
                <td><p id="command" contenteditable="true"></p></td>
            </tr>
        </table>
    </div>
</div>

<script>
    var agentAbilityData, appConfig;

    let refresher = setInterval(refresh, 10000);
    $('.section-profile').bind('destroyed', function() {
        clearInterval(refresher);
    });
    $(document).ready(function () {
        let agentCnt = ($('#agent-table tr').length-1);
        if(agentCnt < 1){
            stream('Looks like you have no agents. You should deploy one now.');
        } else {
            stream('Great, looks like you have '+agentCnt+' agents. You can now go to the Operations tab.')
        }
        $("#agent-beacon").click(function(){
            stream('Set the minimum and maximum seconds the agent will take to beacon home. Beacon timer changes will only affect new agents.');
            $("#agent-beacon-options").slideToggle();
        });
        $("#agent-watchdog").click(function(){
            stream('Set the number of seconds to wait before killing the agent, once the server is unreachable. Set 0 for infinite. Watchdog timer changes will only affect new agents.');
            $("#agent-watchdog-options").slideToggle();
        });
        $("#agent-untrusted").click(function(){
            stream('Set the number of seconds the server will wait before marking a missing agent as untrusted. Untrusted timer changes will affect all agents.');
            $("#agent-untrusted-options").slideToggle();
        });
        $("#agent-implant").click(function(){
            stream('Choose a filename for new agents, without an extension.');
            $("#agent-implant-options").slideToggle();
        });
        $("#agent-bootstrap").click(function(){
            stream('Comma-separated list of ability IDs to be run on a new agent beacon. UUIDs will be validated against current ability list.');
            $("#agent-bootstrap-options").slideToggle();
        });
        $("#agent-deadman").click(function(){
            stream('Comma-separated list of ability IDs to be run when an agent terminates. UUIDs will be validated against current ability list.');
            $("#agent-deadman-options").slideToggle();
        });
    });

    function refresh(){
        function updateTbl(data){
            $('#agent-cnt').text(data.length);
            data.forEach(function(a) {
                let found = false;
                $("#agent-table tr").each(function () {
                    let paw = $(this).attr('id');
                    if(a.paw === paw) {
                        //update existing row
                        if(!a.trusted) {
                            let element = document.getElementById(paw + '-pid');
                            if (element !== null) {
                                element.classList.remove('yellow');
                                element.classList.add('maroon');
                            }
                        }
                        $('#'+paw+'-contact').text(a.contact);
                        found = true;
                    }
                });
                if(!found) {
                    stream('new agent beacon: '+a.paw);

                    let pidButton = $('<button></button>')
                        .prop('id', a.paw + '-pid')
                        .addClass('button-row')
                        .click(function() {
                            showAgentInfo(a.paw)
                        })
                        .text(a.pid);

                    let deleteButton = $('<p></p>')
                        .click( function() {
                            deleteAgent(a.paw)
                        })
                        .html('&#x274C;');

                    let newAgentRow = $('<tr></tr>').prop('id', a.paw)
                        .append($('<td></td>').append($('<p></p>').text(a.paw)))
                        .append($('<td></td>').append($('<p></p>').text(a.host)))
                        .append($('<td></td>').append($('<p></p>').text(a.contact)))
                        .append($('<td></td>').append(pidButton))
                        .append($('<td></td>').append($('<p></p>').text(a.privilege)))
                        .append($('<td></td>').append(deleteButton));

                    $('#agent-table tr:last').after(newAgentRow);
                }
            });
        }
        restRequest('POST', {'index':'agents'}, updateTbl)
    }

    function deleteAgent(paw){
        function deleteAgentCallback(data){
            let element = document.getElementById(paw);
            if (element !== null) {
                element.remove();
            }
            $('#agent-cnt').text(parseInt($('#agent-cnt').text()) - 1);
        }
        stream('Deleting '+paw+'. If this agent is still active, it may pop back up here.');
        restRequest('DELETE', {"index": "agents", "paw": paw}, deleteAgentCallback);
    }

    function updateAgent(){
        function closeAgentModal(){
            document.getElementById('agent-modal').style.display='none';
        }
        let sleepArr = parseSleep($('#modal-sleep').val().replace(/\s/g, ""));
        let data = {
            'index': 'agents',
            'paw': $('#modal-paw').text(),
            'group': $('#modal-group').val(),
            'watchdog': parseInt($('#modal-watchdog').val()),
            'pending_contact': $('#modal-contact').val()
        };
        if (sleepArr.length !== 0) {
            data["sleep_min"] = parseInt(sleepArr[0]);
            data["sleep_max"] = parseInt(sleepArr[1]);
        }
        restRequest('PUT', data, closeAgentModal);
    }

    function saveGlobalAgent() {
        let globalBootstrap = $('#globalBootstrap').val();
        let globalDeadman = $('#globalDeadman').val();
        let globalImplantName = $('#globalImplantName').val();
        let globalMinsleep = $('#globalSleepMin').val();
        let globalMaxsleep = $('#globalSleepMax').val();
        let globalWatchdog = $('#globalWatchdog').val();
        let globalUntrusted = $('#globalUntrusted').val();
        let data = {"index": "agents","sleep_min":parseInt(globalMinsleep),"sleep_max":parseInt(globalMaxsleep),
            "watchdog":parseInt(globalWatchdog),"untrusted":parseInt(globalUntrusted),"implant_name":globalImplantName,
            "bootstrap_abilities":globalBootstrap,"deadman_abilities":globalDeadman};
        stream('Agent configuration has been updated!');
        restRequest('PUT', data, saveGlobalAgentCallback);
    }
    
    function saveGlobalAgentCallback(data) {
        let invalid_bootstrap_abilities = [];
        let invalid_deadman_abilities = [];
        let bootstrap_abilities = $('#globalBootstrap').val().split(",");
        let deadman_abilities = $('#globalDeadman').val().split(",");
        $.each(bootstrap_abilities, function(index, bootstrap_ability) {
            let ability = bootstrap_ability.trim();
            if ($.inArray(ability, data.bootstrap_abilities) === -1 && ability.length > 0) {
                invalid_bootstrap_abilities.push(ability);
            }
        });
        if (invalid_bootstrap_abilities.length > 0) {
            stream('Ignoring invalid bootstrap abilities: ' + invalid_bootstrap_abilities.join(', '));
        }
        $.each(deadman_abilities, function(index, deadman_ability) {
            let ability = deadman_ability.trim();
            if ($.inArray(ability, data.deadman_abilities) === -1 && ability.length > 0) {
                invalid_deadman_abilities.push(ability);
            }
        });
        if (invalid_deadman_abilities.length > 0) {
            stream('Ignoring invalid deadman abilities: ' + invalid_deadman_abilities.join(', '));
        }
    }


    function killAgent(){
        /**
        Sets the currently displayed agent to terminate by setting its sleep seconds greater than its watchdog.
        **/
        let data = {
            'index': 'agents',
            'paw': $('#modal-paw').text(),
            'watchdog': 1,
            'sleep_min': 3,
            'sleep_max': 3
        };
        if (confirm(`Are you sure you want to terminate agent ${data.paw}?`)){
            restRequest('PUT', data, (data) => {
                document.getElementById('agent-modal').style.display='none';
                stream(`Agent ${data.paw} will self terminate after its next beacon.`);
                let element = document.getElementById(data.paw + '-pid');
                if (element !== null) {
                    element.classList.remove('maroon');
                    element.classList.add('yellow');
                }
            });
        }
    }

    function parseSleep(sleep){
        let patt = new RegExp("\\d+\\/\\d+");
        if (patt.test(sleep)){
            let result = sleep.split("/");
            if (parseInt(result[0]) <= parseInt(result[1])){
                return result;
            }
            return result.reverse();
        }
        return [];
    }

    function showAgentInfo(paw){
        function agentInfoCallback(data){
            let parent = $('#agent-modal');
            let agent = data[0];
            let peer_receivers = "";
            let peer_chain = "";
            let current_contact = agent['contact'];
            parent.find('#modal-paw').text(agent['paw']);
            parent.find('#modal-host').text(agent['host']);
            parent.find('#modal-username').text(agent['username']);
            parent.find('#modal-privilege').text(agent['privilege']);
            parent.find('#modal-last_seen').text(agent['last_seen']);
            parent.find('#modal-exe_name').text(agent['exe_name']);
            parent.find('#modal-created').html(agent['created']);
            parent.find('#modal-group').val(agent['group']);
            parent.find('#modal-sleep').val(agent['sleep_min']+'/'+agent['sleep_max']);
            parent.find('#modal-watchdog').val(agent['watchdog']);
            parent.find('#modal-architecture').text(agent['architecture']);
            parent.find('#modal-platform').text(agent['platform']);
            parent.find('#modal-location').text(agent['location']);
            parent.find('#modal-pid').text(agent['pid']);
            parent.find('#modal-ppid').text(agent['ppid']);
            parent.find('#modal-executors').text(JSON.stringify(agent['executors']));
            parent.find('#modal-watchdog').text(agent['watchdog']);
            parent.find('#modal-host_ip_addrs').text(JSON.stringify(agent['host_ip_addrs']));

            // Set up contact selection
            let num_contacts = agent['available_contacts'].length;
            let contact_elem = parent.find('#modal-contact');
            contact_elem.empty();
            contact_elem.prepend(
                $('<option></option>')
                    .addClass('contact-option')
                    .val(current_contact)
                    .text(current_contact)
            );
            for (i = 0; i < num_contacts; i++) {
                let curr = agent['available_contacts'][i];
                if (curr != current_contact) {
                    contact_elem.append(
                        $('<option></option>')
                            .addClass('contact-option')
                            .val(curr)
                            .text(curr)
                    );
                }
            }
            contact_elem.val(current_contact);

            // Set up peer-to-peer proxy receiver information display.
            parent.find('#modal-peer_receivers').empty();
            if (Object.keys(agent['proxy_receivers']).length == 0) {
                parent.find('#modal-peer_receivers').text('No local peer-to-peer proxy receivers active.');
            } else {
                let peerReceiversTable = $('<table/>');
                for (var key in agent['proxy_receivers']) {
                    $('<tr/>')
                        .append($('<td/>').append($('<i/>').text(key)))
                        .append($('<td/>').append($('<p/>')))
                        .appendTo(peerReceiversTable);

                    for (i = 0; i < agent['proxy_receivers'][key].length; i++) {
                        $('<tr/>')
                            .append($('<td/>').append($('<p/>')))
                            .append($('<td/>').append($('<p/>').text(agent['proxy_receivers'][key][i])))
                            .appendTo(peerReceiversTable);
                    };
                };
                parent.find('#modal-peer_receivers').append(peerReceiversTable);
            };

            // Set up peer-to-peer proxy chain information display.
            parent.find('#modal-peer_chain').empty();
            let proxy_chain_length = agent['proxy_chain'].length;
            if (proxy_chain_length == 0) {
                parent.find('#modal-peer_chain').text('Not using peer agents to reach C2.');
            } else {
                let headerRow = $('<tr></tr>').html(
                    '<th style="color:white"><i>Hop Index</i></th>'
                    + '<th style="color:white"><i>Peer Paw</i></th>'
                    + '<th style="color:white"><i>Peer Receiver Address</i></th>'
                    + '<th style="color:white"><i>Peer Receiver Protocol</i></th>'
                );
                let peerChainTable = $('<table></table>').prop('cellspacing', 15).append(headerRow);

                // Proxy chain rows
                for (i = 0; i < proxy_chain_length; i++) {
                    let peerRow = $('<tr></tr>').append(
                        $('<td></td>')
                            .prop('style', 'text-align:center')
                            .append($('<i></i>').text(i))
                    );
                    for (j = 0; j < agent['proxy_chain'][i].length; j++) {
                        $('<td></td>')
                            .append($('<p></p>').text(agent['proxy_chain'][i][j]))
                            .appendTo(peerRow);
                    }
                    peerChainTable.append(peerRow);
                };
                parent.find('#modal-peer_chain').append(peerChainTable);
            };
            parent.show();
        }
        restRequest('POST', {'index':'agents','paw':paw}, agentInfoCallback)
    }

    function displayCommand(){
        function displayMe(data){
            agentAbilityData = data["abilities"];
            appConfig = data["app_config"];
            data["abilities"].forEach(function(ability, agentIdx) {
                addConfigArguments(ability.command);
                variations.append(buildAbilityTemplate(ability.platform, ability.executor, ability.description,
                                                       ability.command, agentIdx, null));
                ability.variations.forEach(function(v, variationIdx) {
                    addConfigArguments(v.command);
                    variations.append(buildAbilityTemplate(ability.platform, ability.executor, v.description,
                                                           v.command, agentIdx, variationIdx));
                });
            });
            replaceProps();
            $("#conf-arguments").show();
            populatePlatforms(data["abilities"]);


        }
        function populatePlatforms(abilities){
            $("#chosen-platform").empty();
            $("#chosen-platform").append("<option selected value=''>All platforms</option>");
            abilities.forEach(function(ability) {
                if ($("#chosen-platform option[value='" + ability.platform + "']").length == 0) {
                    $("#chosen-platform").append('<option value="' + ability.platform + '">' + ability.platform + '</option>')
                }
            });
        }
        let variations = $('#variations');
        variations.empty();
        $("#conf-arguments").empty();
        restRequest('POST', {'index':'agent_configuration','ability_id':$('#chosen-agent option:selected').val()}, displayMe);
    }

    function filterCommands(){
        let platform = $("#chosen-platform").val();
        $("#variations").children().each(function() {
            if ($(this).data('platform') == platform || platform == '') {
                $(this).show();
            } else {
                $(this).hide();
            }
        });
    }

    function buildAbilityTemplate(platform, executor, description, command, agentIdx, variationIdx){
        let template = $('#variation-template').clone();
        template.attr('id', 'command-variation');
        template.find('#description').text(description + ' ('+executor+')');
        template.find('#command').text(command);
        template.find('#command').addClass('command');
        template.find('#command').data('agent-index', agentIdx);
        template.find('#command').data('variation-index', variationIdx);
        template.find('#platform-icon').attr('src', '/gui/img/'+platform+'.png');
        template.data('platform', platform);
        template.show();
        return template;
    }

    function addConfigArguments(command) {
        let args = Array.from(command.matchAll(/#{(.+?)}/g));
        args.forEach(function(arg) {
            let capturedGroup = arg[1];
            if ($(".config-arg[data-contact='" + capturedGroup + "']").length == 0) {
                let argHTML = "<tr class='config-arg' data-contact='" + capturedGroup +"'><td><label>" + capturedGroup + "</label></td><td>" +
                    "<input placeholder='" + appConfig[capturedGroup] + "' oninput='replaceProps()'></td></tr>";
                $("#conf-arguments").append(argHTML);
            }
        });
    }

    function replaceProps() {
        $(".command").each(function(i, elem) {
            let command = "";
            let agentIdx = $(elem).data("agent-index");
            let variationIdx = $(elem).data("variation-index");
            if ($(elem).data("variation-index") != null) {
                command = agentAbilityData[agentIdx].variations[variationIdx].command;
            }
            else {
                command = agentAbilityData[agentIdx].command;
            }
            let args = Array.from(command.matchAll(/#{(.+?)}/g));
            args.forEach(function(arg) {
                let capturedGroup = arg[1];
                let input = $("#conf-arguments label:contains('" + capturedGroup + "')").closest("tr").find("input");
                let inputValue = input.val() ? input.val() : input.attr("placeholder");
                command = command.replace(arg[0], inputValue);
            })
            $(elem).text(command);
        })
    }

    //# sourceURL=agents.js
</script>
